// ===
// @Author       : 六网
// @Date         : 2022-04-30 15:59:45
// @LastEditTime : 2022-05-02 17:21:49
// @LastEditors  : 六网
// @FilePath     : /cec-vision-cpp/modules/src/camera.cpp
// @Gitee        : https://gitee.com/sixspace
// @Description  : 工业相机模块源文件，获取图片，设置曝光度等
// ===

#include "camera.hpp"
#include "CameraApi.h"
#include "except.hpp"

using namespace cv;

class CameraPrivate {

public:
    CameraPrivate();

    ~CameraPrivate();

    tSdkCameraDevInfo m_tCameraEnumList = {};
    tSdkCameraCapbility m_tCameraCapability = {};
    tSdkFrameHead m_tCameraFrameHead = {};
    bool m_CameraDevStatus;
    bool m_CameraOpenStatus;
    bool m_CameraPlayStatus;
    bool m_CameraSoTrStatus;
    int m_iHCamera;
    BYTE *m_bypFrameBuffer;
    std::shared_ptr<spdlog::logger> m_outLogger;
    std::shared_ptr<spdlog::logger> m_errLogger;

    bool printLog(int &statusCode, const std::string &sucText, const std::string &errText);
};

CameraPrivate::CameraPrivate() :
        m_CameraDevStatus(false), m_CameraOpenStatus(false), m_CameraPlayStatus(false), m_CameraSoTrStatus(false),
        m_iHCamera(0) {
    this->m_bypFrameBuffer = nullptr;

    this->m_outLogger = spdlog::stdout_color_st("CameraOut");
    this->m_outLogger->set_level(spdlog::level::info);
    this->m_outLogger->flush_on(spdlog::level::info);

    this->m_errLogger = spdlog::stderr_color_st("CameraErr");
    this->m_errLogger->set_level(spdlog::level::err);
    this->m_errLogger->flush_on(spdlog::level::err);

}

CameraPrivate::~CameraPrivate() {
    if (this->m_bypFrameBuffer != nullptr) {
        CameraAlignFree(this->m_bypFrameBuffer);
        this->m_bypFrameBuffer = nullptr;
    }
}

bool CameraPrivate::printLog(int &statusCode, const std::string &sucText, const std::string &errText) {
    if (statusCode != CAMERA_STATUS_SUCCESS) {
        auto message = CameraGetErrorString(statusCode);
        this->m_outLogger->error(errText);
        this->m_errLogger->error(message);
        return false;
    } else {
        if (!sucText.empty())
            this->m_outLogger->info(sucText);
        statusCode = -1;
        return true;
    }
}

Camera::Camera(const std::string &configsFile) : m_ptr(new CameraPrivate()) {
    int iStatusCode = -1;
    int iCameraCounts = 1;

    iStatusCode = CameraEnumerateDevice(&(m_ptr->m_tCameraEnumList), &iCameraCounts);
    if (iCameraCounts <= 0) {
        auto message = CameraGetErrorString(iStatusCode);
        m_ptr->m_outLogger->error("设备未找到!");
        m_ptr->m_errLogger->error(message);
        m_ptr->m_CameraDevStatus = false;
        throw CecException("设备未找到!");
    } else {
        m_ptr->m_outLogger->info("成功找到相机->数量:{}", iCameraCounts);
        m_ptr->m_CameraDevStatus = true;
        iStatusCode = 0;
    }
    iStatusCode = CameraInit(&(m_ptr->m_tCameraEnumList), -1, -1, &(m_ptr->m_iHCamera));
    m_ptr->m_CameraDevStatus = m_ptr->printLog(iStatusCode, "设备初始化成功!", "设备初始化失败!");
    if (m_ptr->m_CameraDevStatus) {
        char *p = const_cast<char *>(configsFile.c_str());
        iStatusCode = CameraReadParameterFromFile(m_ptr->m_iHCamera, p);
        m_ptr->m_CameraDevStatus = m_ptr->printLog(iStatusCode, "设备参数加载成功!", "设备参数加载失败!");
    }
}

Camera::~Camera() {
    if (m_ptr->m_CameraOpenStatus)
        this->close();
    delete m_ptr;
}

bool Camera::open(bool soft) {
    int iStatusCode = -1;
    if (!m_ptr->m_CameraOpenStatus) {
        if (m_ptr->m_CameraDevStatus) {
//            iStatusCode = CameraSetFrameSpeed(m_ptr->m_iHCamera, 1);
//            m_ptr->printLog(iStatusCode, "设备高帧率设置成功!", "设备高帧率设置失败!");
            CameraRstTimeStamp(m_ptr->m_iHCamera);
            CameraGetCapability(m_ptr->m_iHCamera, &(m_ptr->m_tCameraCapability));
            CameraSetIspOutFormat(m_ptr->m_iHCamera, CAMERA_MEDIA_TYPE_BGR8);
            if (soft) {
                iStatusCode = CameraSetTriggerMode(m_ptr->m_iHCamera, 1);
                m_ptr->printLog(iStatusCode, "设备软触发设置成功!", "设备软触发设置失败!");
                CameraSetTriggerCount(m_ptr->m_iHCamera, 1);
                m_ptr->m_CameraSoTrStatus = true;
            } else {
                iStatusCode = CameraSetTriggerMode(m_ptr->m_iHCamera, 0);
                bool status = m_ptr->printLog(iStatusCode, "设备自动触发设置成功!", "设备自动触发设置失败!");
                if (!status) {
                    m_ptr->m_outLogger->error("设备自动触发设置失败, 相机打开失败!");
                    m_ptr->m_CameraSoTrStatus = false;
//                    return status;
                    throw CecException("相机打开失败!");
                }
                m_ptr->m_CameraSoTrStatus = false;
            }
            int frameWidth = m_ptr->m_tCameraCapability.sResolutionRange.iWidthMax;
            int frameHeight = m_ptr->m_tCameraCapability.sResolutionRange.iHeightMax;
            m_ptr->m_bypFrameBuffer = CameraAlignMalloc(frameWidth * frameHeight * 3, 16);
            if (m_ptr->m_bypFrameBuffer == nullptr) {
                m_ptr->m_errLogger->error("设备图像帧内存空间申请失败!");
                m_ptr->m_outLogger->error("相机打开失败!");
                throw CecException("设备图像帧内存空间申请失败, 相机打开失败!");
            } else {
                m_ptr->m_outLogger->info("设备图像帧内存空间申请成功!");
            }
            m_ptr->m_outLogger->info("相机打开成功!");
            m_ptr->m_CameraOpenStatus = true;
            return true;
        } else {
            m_ptr->m_outLogger->error("相机初始化阶段失败，打开失败!");
            return false;
        }
    } else {
        m_ptr->m_outLogger->warn("相机已经打开，请勿重复打开!");
        return false;
    }
}

bool Camera::close() {
    int iStatusCode = -1;
    if (m_ptr->m_CameraOpenStatus) {
        iStatusCode = CameraStop(m_ptr->m_iHCamera);
        m_ptr->printLog(iStatusCode, "相机停止成功!", "相机停止失败!");
        iStatusCode = CameraUnInit(m_ptr->m_iHCamera);
        int status = m_ptr->printLog(iStatusCode, "相机反初始化成功!", "相机反初始化失败!");
        if (status) {
            m_ptr->m_CameraPlayStatus = false;
            m_ptr->m_CameraOpenStatus = false;
        }
        if (m_ptr->m_bypFrameBuffer != nullptr) {
            CameraAlignFree(m_ptr->m_bypFrameBuffer);
            m_ptr->m_outLogger->info("设备图像帧内存空间释放成功!");
            m_ptr->m_bypFrameBuffer = nullptr;
            m_ptr->m_outLogger->info("相机关闭成功!");
            return true;
        } else {
            m_ptr->m_outLogger->warn("设备图像帧内存空间释放失败, 因为已经释放过了");
            m_ptr->m_outLogger->critical("相机关闭成功，但内存空间释放异常!!");
            m_ptr->m_errLogger->critical("相机关闭成功，但内存空间释放异常!!");
            return false;
        }
    } else {
        m_ptr->m_outLogger->warn("相机已经关闭，请勿重复关闭!");
        return false;
    }
}

bool Camera::play() {
    int iStatusCode = -1;
    if (m_ptr->m_CameraOpenStatus) {
        iStatusCode = CameraPlay(m_ptr->m_iHCamera);
        bool status = m_ptr->printLog(iStatusCode, "相机成功开始采集图像!", "相机开始采集图像失败!");
        if (status) {
            m_ptr->m_CameraPlayStatus = true;
        } else {
            m_ptr->m_CameraPlayStatus = false;
        }
        return status;
    } else {
        m_ptr->m_outLogger->warn("相机已经开始采集图像，清勿重复开启!");
        return false;
    }
}

bool Camera::stop() {
    int iStatusCode = -1;
    if (m_ptr->m_CameraPlayStatus) {
        iStatusCode = CameraPause(m_ptr->m_iHCamera);
        bool status = m_ptr->printLog(iStatusCode, "相机暂停成功!", "相机暂停失败!");
        if (status) {
            m_ptr->m_CameraPlayStatus = false;
        }
        return status;
    } else {
        m_ptr->m_outLogger->warn("相机没有开始采集图像，请开始后再停止!");
        return false;
    }
}

std::tuple<bool, Mat, int> Camera::read(int waitTime) {
    UINT timeStampL;
    UINT timeStampH;
    int iStatusCode = -1;
    int timeAll = 0;
    BYTE *tempBuffer;
    Mat image;
    if (m_ptr->m_CameraPlayStatus) {
        iStatusCode = CameraGetImageBufferPriority(
                m_ptr->m_iHCamera, &(m_ptr->m_tCameraFrameHead),
                &tempBuffer, waitTime, CAMERA_GET_IMAGE_PRIORITY_NEWEST
        );
        if (iStatusCode == CAMERA_STATUS_SUCCESS) {
            iStatusCode = CameraGetFrameTimeStamp(m_ptr->m_iHCamera, &timeStampL, &timeStampH);
            if (m_ptr->printLog(iStatusCode, "", "图像时间戳获取失败!"))
                timeAll = (int) (timeStampH << 8 | timeStampL);
            else
                timeAll = 0;
            CameraImageProcess(m_ptr->m_iHCamera, tempBuffer, m_ptr->m_bypFrameBuffer, &(m_ptr->m_tCameraFrameHead));
            image = Mat(
                    Size(m_ptr->m_tCameraFrameHead.iWidth, m_ptr->m_tCameraFrameHead.iHeight),
                    CV_8UC3,
                    m_ptr->m_bypFrameBuffer
            );
            CameraReleaseImageBuffer(m_ptr->m_iHCamera, m_ptr->m_bypFrameBuffer);
            return std::make_tuple(true, image, timeAll);
        } else if (iStatusCode != CAMERA_STATUS_TIME_OUT) {
            auto &&message = CameraGetErrorString(iStatusCode);
            m_ptr->m_outLogger->error("相机读取图像发生未知错误!");
            m_ptr->m_errLogger->error(message);
//            return std::make_tuple(false, image, timeAll);
            throw CecException(message);
        } else {
            return std::make_tuple(false, image, timeAll);
        }
    } else {
        m_ptr->m_outLogger->warn("相机未开始采集图像数据，获取图片数据失败!");
        return std::make_tuple(false, image, timeAll);
    }
}

std::tuple<bool, float, float, float> Camera::getExposureTimeRange() {
    double min = -1, max = -1, step = -1;
    int iStatusCode = CameraGetExposureTimeRange(m_ptr->m_iHCamera, &min, &max, &step);
    return std::make_tuple(m_ptr->printLog(
            iStatusCode,
            fmt::format("曝光时间范围获取成功->min:{} max:{} step:{}", min, max, step),
            fmt::format("曝光时间范围获取失败!")), min, max, step);
}

bool Camera::setExposureTime(float times) {
    int iStatusCode = CameraSetExposureTime(m_ptr->m_iHCamera, times * 1000);
    return m_ptr->printLog(iStatusCode, "曝光时间设置成功!", "曝光时间设置失败!");
}

std::tuple<bool, double> Camera::getExposureTime() {
    double exposureTime;
    int iStatusCode = CameraGetExposureTime(m_ptr->m_iHCamera, &exposureTime);
    return std::make_tuple(m_ptr->printLog(
            iStatusCode,
            fmt::format("曝光时间获取成功->time:{}", time),
            fmt::format("曝光时间获取失败!")
    ), exposureTime);
}

bool Camera::setFrameSpeedMode(bool high) {
    int iStatusCode = CameraSetFrameSpeed(m_ptr->m_iHCamera, high);
    return m_ptr->printLog(iStatusCode, "高帧率模式设置成功!", "高帧率模式设置失败!");
}

bool Camera::setAnalogGain(int num) {
    int iStatusCode = CameraSetAnalogGain(m_ptr->m_iHCamera, num);
    return m_ptr->printLog(iStatusCode, "模拟增益设置成功!", "模拟增益设置失败!");
}

bool Camera::setLutMode(int mode) {
    int iStatusCode = CameraSetLutMode(m_ptr->m_iHCamera, mode);
    return m_ptr->printLog(iStatusCode, "Lut曲线设置成功!", "Lut曲线设置失败!");
}

bool Camera::setGamma(int num) {
    int iStatusCode = CameraSetGamma(m_ptr->m_iHCamera, num);
    return m_ptr->printLog(iStatusCode, "Gamma值设置成功!", "Gamma值设置失败!");
}

bool Camera::setContrast(int num) {
    int iStatusCode = CameraSetContrast(m_ptr->m_iHCamera, num);
    return m_ptr->printLog(iStatusCode, "对比度设置成功!", "对比度设置失败!");
}

std::tuple<bool, int> Camera::getFrameTimeStamp() {
    UINT *timeStampL;
    UINT *timeStampH;
    int timeAll;
    int iStatusCode = CameraGetFrameTimeStamp(m_ptr->m_iHCamera, timeStampL, timeStampH);
    timeAll = (int) ((*timeStampH) << 8 | (*timeStampL));
    return std::make_tuple(
            m_ptr->printLog(iStatusCode, "", "图像时间戳获取失败!"),
            timeAll);
}

bool Camera::setRoi(std::tuple<int, int> offset, std::tuple<int, int> resolution) {
    tSdkImageResolution ap;
    ap.iIndex = 0xff;
    std::tie(ap.iHOffsetFOV, ap.iVOffsetFOV) = offset;
    std::tie(ap.iWidthFOV, ap.iHeightFOV) = resolution;
    std::tie(ap.iWidth, ap.iHeight) = resolution;
    int iStatusCode = CameraSetImageResolution(m_ptr->m_iHCamera, &ap);
    return m_ptr->printLog(iStatusCode, "Roi设置成功!", "Roi设置失败!");
}

bool Camera::softTrigger() {
    if (m_ptr->m_CameraSoTrStatus) {
        int iStatusCode = CameraSoftTrigger(m_ptr->m_iHCamera);
        if (iStatusCode != CAMERA_STATUS_SUCCESS) {
            m_ptr->m_outLogger->error("设备内部问题，软触发失败，请检查错误信息!");
            auto &&errorStr = CameraGetErrorString(iStatusCode);
            m_ptr->m_errLogger->error(errorStr);
            throw CecException(errorStr);
        }
        return true;
    } else {
        m_ptr->m_outLogger->warn("软触发失败，请开启软触发模式后再尝试!");
        return false;
    }
}

bool Camera::setTriggerCount(int nums) {
    if (m_ptr->m_CameraSoTrStatus) {
        int iStatusCode = CameraSetTriggerCount(m_ptr->m_iHCamera, nums);
        return m_ptr->printLog(iStatusCode, "触发次数设置成功!", "触发次数设置失败!");
    } else {
        m_ptr->m_outLogger->warn("触发次数设置失败，请开启软触发模式后再尝试!");
        return false;
    }
}

bool Camera::openSoftTrigger() {
    if (!(m_ptr->m_CameraSoTrStatus)) {
        int iStatusCode = CameraSetTriggerMode(m_ptr->m_iHCamera, 1);
        return m_ptr->printLog(iStatusCode, "开启软触发成功!", "开启软触发失败!");
    } else {
        m_ptr->m_outLogger->warn("开启软触发失败，请勿重复开启!");
        return false;
    }
}

bool Camera::closeSoftTrigger() {
    if (m_ptr->m_CameraSoTrStatus) {
        int iStatusCode = CameraSetTriggerMode(m_ptr->m_iHCamera, 0);
        return m_ptr->printLog(iStatusCode, "关闭软触发进入自动触发模式成功!", "关闭软触发失败!");
    } else {
        m_ptr->m_outLogger->warn("关闭软触发失败，请勿重复关闭!");
        return false;
    }
}

Camera *Camera::getInstance(const std::string &configsFile) {
    static Camera instance(configsFile);
    return &instance;
}